import { AclType } from "@/core/pinia/interface";
import { ElTooltipProps, TableColumnCtx, TableInstance } from "element-plus";
import { EpPropMergeType } from "element-plus/es/utils";
import { Subject } from "rxjs";
import { DefineComponent } from "vue";
import { NzSafeAny } from "../sf/type";

export interface ITableColumnButton<T = Record<string, NzSafeAny>> {
  /**
   * 文本
   */
  text?: string;
  /**
   * 图标
   */
  icon?: string;
  /**
   * 按钮类型
   * - `none` 无任何互动
   * - `del` 删除，默认开启 `pop: true`
   * - `modal` 对话框，需要指定 `component` 才会生效
   * - `static` 静态对话框，需要指定 `component` 才会生效
   * - `drawer` 抽屉，需要指定 `component` 才会生效
   * - `link` 链接，当 `click` 返回字符串时自动调用 `navigateByUrl` 导航
   * - `divider` 分割线
   */
  type?: "none" | "del" | "link";

  /**
   * 分割线
   */
  divider?: "" | "solid" | "dashed";
  /**
   * 点击回调
   * - Function
   *  - `type=modal` 只会在当有传回值时才会触发回调
   * - reload：重新刷新当前页
   * - load：重新加载数据，并重置页码为：`1`
   *
   * @todo Bad parameter design
   */
  click?: (record: T, st: STExposeComponent, index?: number) => void;
  /**
   * 气泡确认框参数
   */
  pop?: boolean;
  /**
   * 下拉菜单，当存在时以 `dropdown` 形式渲染
   * - 只支持一级
   */
  children?: ITableColumnButton<T>[];
  /**
   * 权限，等同 [ACLCanType](https://ng-alain.com/acl/getting-started/#ACLCanType) 参数值
   */
  acl?: AclType;
  /**
   * Conditional expression
   *
   * @todo Bad parameter design
   */
  iif?: (item: T) => boolean;

  color?: string;

  tooltip?: string;

  [key: string]: any;
}
export interface ITableColumn<T = Record<string, NzSafeAny>> {
  /**
   * 对应列的类型。 如果设置了selection则显示多选框；'
   *  如果设置了 index 则显示该行的索引（从 1 开始计算）
   * ； 如果设置了 expand 则显示为一个可展开的按钮
   */
  type?: "selection" | "index" | "date" | "tags" | "tag" | "icon";
  /**
   * 当type 为date 时的日期转换
   * 默认为YYYY-MM-DD hh:mm:ss
   */
  dateFormat?: string;

  /**
   * 当type为tag 时的有效
   * 形如:{
   *   0:{text:string,color:string},
   *   1:{text:string,color:string}
   * }
   */
  tags?: {
    [key: string | number]: {
      text: string;
      color?: string;
      type?: "success" | "info" | "warning" | "danger" | "";
    };
  };

  tag?: "success" | "info" | "warning" | "danger" | "";

  /**
   * 改列的隐藏
   */
  iff?: (data: T) => boolean;
  /**
   * 如果设置了 type=index，可以通过传递 index 属性来自定义索引
   */
  index?: number;

  /**
   * 显示的标题
   */
  label?: string;
  /**
   * column 的 key， column 的 key，
   *  如果需要使用 filter-change 事件，
   * 则需要此属性标识是哪个 column 的筛选条件
   */
  columnKey?: string;
  /**
   * 字段名称 对应列内容的字段名， 也可以使用 property属性
   */
  prop?: string;
  /**
   * 对应列的宽度
   */
  width?: string | number;
  /**
   * 对应列的最小宽度， 对应列的最小宽度，
   * 与 width 的区别是 width 是固定的，
   * min-width 会把剩余宽度按比例分配给设置了 min-width 的列
   */
  minWidth?: string | number;
  /**
   * 列是否固定在左侧或者右侧。 true 表示固定在左侧
   */
  fixed?: "left" | "right" | boolean;

  /**
   * 对应列是否可以排序， 如果设置为 'custom'，
   * 则代表用户希望远程排序，需要监听 Table 的 sort-change 事件
   */
  sortable?: boolean | string;

  /**
   * 指定数据按照哪个属性进行排序，
   * 仅当sortable设置为true的时候有效。
   * 应该如同 Array.sort 那样返回一个 Number
   */
  sortMethod?: (a: any, b: any) => number;
  /**
   *指定数据按照哪个属性进行排序，
   *仅当 sortable 设置为 true 且没有设置 sort-method 的时候有效。
   * 如果 sort-by 为数组，
   *则先按照第 1 个属性排序，如果第 1 个相等，再按照第 2 个排序，以此类推
   */
  sortBy?: string | string[];
  /**
   *数据在排序时所使用排序策略的轮转顺序，
   *仅当 sortable 为 true 时有效。 需传入一个数组，
   *随着用户点击表头，该列依次按照数组中元素的顺序进行排序
   */
  sortOrders?: string[];

  /**
   *用来格式化内容
   */
  formatter?: (row: T, index: number) => NzSafeAny;
  /**
   * 当内容过长被隐藏时显示 tooltip
   */
  showOverflowTooltip?:
    | boolean
    | Pick<
        ElTooltipProps,
        | "effect"
        | "enterable"
        | "hideAfter"
        | "offset"
        | "placement"
        | "popperClass"
        | "popperOptions"
        | "showAfter"
        | "showArrow"
      >;
  /**
   * 对齐方式
   */
  align?: "left" | "center" | "right";
  /**
   * 表头对齐方式， 若不设置该项，则使用表格的对齐方式
   */
  headerAlign?: "left" | "center" | "right";
  /**
   * 列的 className
   */
  className?: string;
  /**
   * 当前列标题的自定义类名
   */
  labelClassName?: string;
  /**
   * 仅对 type=selection 的列有效，类型为 Function，
   * Function 的返回值用来决定这一行的 CheckBox 是否可以勾选
   */
  selectable?: (row: T, index: number) => boolean;
  /**
   * 数据刷新后是否保留选项，仅对  type=selection 的列有效，
   * 请注意， 需指定 row-key 来让这个功能生效。
   */
  reserveSelection?: boolean;
  /**
   * 数据过滤的选项， 数组格式，数组中的元素需要有 text 和 value 属性。
   *  数组中的每个元素都需要有 text 和 value 属性。
   */
  filters?: { text: string; value: string }[];
  /**
   * 过滤弹出框的定位
   */
  filterPlacement?:
    | "top"
    | "top-start"
    | "top-end"
    | "bottom"
    | "bottom-start"
    | "bottom-end"
    | "left"
    | "left-start"
    | "left-end"
    | "right"
    | "right-start"
    | "right-end";
  /**
   * 数据过滤的选项是否多选
   */
  filterMultiple?: boolean;
  /**
   * 数据过滤使用的方法， 如果是多选的筛选项，
   * 对每一条数据会执行多次，任意一次返回 true 就会显示
   */
  filterMethod?: (value: string, row: T, column: TableColumnCtx<T>) => boolean;

  /**
   * 选中的数据过滤项，
   * 如果需要自定义表头过滤的渲染方式，可能会需要此属性。
   */
  filteredValue?: string[];
  /**
   * aCL 权限控制
   */
  acl?: AclType;
  /**
   * 操作按钮
   */
  buttons?: ITableColumnButton<T>[];
  /**
   * 该列是否隐藏
   */
  hidden?: boolean;

  children?: ITableColumn[];
}

export interface ITableOptions<T = Record<string, NzSafeAny>> {
  /**
   * 可将表格内容 highlight 显示，方便区分「成功、信息、警告、危险」等内容。
   * 可以通过指定 Table 组件的 row-class-name 属性来为 Table 中的某一行添加 class，
   * 这样就可以自定义每一行的样式了
   */
  rowClassName?: (row: T, index: number) => string;
  /**
   * 点击某一列的回调函数
   */
  currentChange?: (row: T) => void;
  /**
   * 点击复选框，返回所有被选中的列的数据
   */
  selectionChange?: (row: T[]) => void;
  /**
   * 默认排序
   */
  defaultSort?: {
    prop: keyof T;
    order: "descending" | "ascending";
  };
  /**
   * Table 的高度， 默认为自动高度。
   * 如果 height 为 number 类型，单位 px；
   * 如果 height 为 string 类型，则这个高度会设置为 Table
   * 的 style.height 的值，Table 的高度会受控于外部样式。
   */
  height?: string | number;

  /**
   * Table 的最大高度。 合法的值为数字或者单位为 px 的高度。
   */
  maxHeight?: string | number;

  /**
   * 是否为斑马纹 table
   */
  noStripe?: boolean;

  /**
   * 是否带有纵向边框
   */
  noBorder?: boolean;

  /**
   * 	Table 的尺寸
   */
  size?: EpPropMergeType<
    StringConstructor,
    "" | "default" | "large" | "small",
    never
  >;

  /**
   * 	列的宽度是否自撑开
   */
  fit?: boolean;

  /**
   * 	是否显示表头
   */
  noShowHeader?: boolean;

  /**
   * 	是否要高亮当前行
   */
  highlightCurrentRow?: boolean;
  /**
   * 空数据提示
   */
  emptyText?: string;
  /**
   * 列表对象
   */
  column?: ITableColumn<T>[];
  /**
   * 是否固定表头
   */
  fixed?: boolean;
  /**
   * 数据源
   */
  data?: string | T[] | TablePageData<T>[];

  /**
   * 是否启动分页配置
   */
  noEnabledPage?: boolean;
  pi?: number;
  ps?: number;
  /**
   * 分页配置
   */
  pages?: TPages;
  /**
   * 操作源设置
   */
  operator?: TOporaterOption | "none";
  /**
   * 嵌套tree
   */
  expand?: boolean;

  expandChange?: (row: T, index: number) => void | string;

  /**
   * 嵌套懒加载
   */
  lazy?: boolean;
  load?: (row: T, treeNode: unknown, resolve: (date: T[]) => void) => void;
  /** TREE-ST 父参数 */
  parent?: T;
  onloadChildren?: (val: any) => boolean;
}

export interface ITreeTableOption<T = Record<string, NzSafeAny>>
  extends ITableOptions<T> {
  /**
   * 是否展示子分页
   */
  noPage?: boolean;
}

export type TPages = {
  pageSizes?: number[];
  showJumper?: boolean;
  totalKey?: string;
  currentPage?: string;
  hideOnSinglePage?: boolean;
  record?: string;
  limit?: string;
};

export type TablePageData<T = Record<string, NzSafeAny>> = {
  total: number;
  pi: number;
  ps: number;
  list: T[];
};

export type TPagination = {
  pageSizes?: number[];
  hideOnSinglePage?: boolean;
  showJumper?: boolean;
  total: number;
  pi: number;
  ps: number;
};

export type ColumnKeys = {
  label: string;
  acl: AclType;
  value: string;
  checked: boolean;
  type?: string;
};

export type TOporaterOption = {
  column: ColumnKeys[];
  refHide?: boolean;
  addIcon?: DefineComponent;
  addTitle?: string;
  addCode?: string;
  addHide?: boolean;
  delIcon?: DefineComponent;
  delTitle?: string;
  delCode?: string;
  delHide?: boolean;
  buttonsTitle?: string;
  buttons?: {
    title: string;
    icon?: DefineComponent;
    code?: string;
    click?: () => void;
  }[];
};

export type STExposeComponent = {
  reset: (val?: Record<string, NzSafeAny>) => void;
  setData: (path: string) => void;
  del: Subject<string[]>;
  add: Subject<TableInstance>;
  search: Subject<boolean>;
  resetChild: (val?: Record<string, NzSafeAny>) => void;
  tableRef: {
    value: TableInstance;
  };
};
